#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import cv2
import win32gui
import win32ui
import win32con
import win32api
from pylab import *
from ctypes import windll


# 从x,y位置,截图长宽w,h
# 结果图片啊是个bmp编码,文件较大
# 如果只传入文件名,则截取全屏
def capture_screen_win32(filename, x=None, y=None, w=None, h=None):
    if not filename.endswith('.bmp'):
        filename += '.bmp'  # 没有后缀时添加一个
    if x is None or y is None or w is None or h is None:
        display_dev = win32api.EnumDisplayMonitors(None, None)
        w, h = display_dev[0][2][2], display_dev[0][2][3]
        x, y = 0, 0  # 如果没有传入参数,则截取全屏
    hwnd = 0  # 窗口的编号，0号表示当前活跃窗口
    # 根据窗口句柄获取窗口的设备上下文DC（Divice Context）
    hwnd_dc = win32gui.GetWindowDC(hwnd)
    # 根据窗口的DC获取mfcDC
    mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
    # mfcDC创建可兼容的DC
    save_dc = mfc_dc.CreateCompatibleDC()
    # 创建bigmap准备保存图片
    save_bitmap = win32ui.CreateBitmap()
    # 为bitmap开辟空间
    save_bitmap.CreateCompatibleBitmap(mfc_dc, w, h)
    # 高度saveDC，将截图保存到saveBitmap中
    save_dc.SelectObject(save_bitmap)
    # 截取从左上角（x，y）长宽为（w，h）的图片
    save_dc.BitBlt((0, 0), (w, h), mfc_dc, (x, y), win32con.SRCCOPY)
    save_bitmap.SaveBitmapFile(save_dc, filename)
    # 下面释放资源
    mfc_dc.DeleteDC()
    save_dc.DeleteDC()
    win32gui.ReleaseDC(hwnd, hwnd_dc)
    win32gui.DeleteObject(save_bitmap.GetHandle())


# 鼠标左键点击一次
def mouse_click(click_y=None, click_x=None):
    if click_x is not None and click_y is not None:
        windll.user32.SetCursorPos(click_x, click_y)
        time.sleep(1)  # 每次点击都带停顿,避免操作太快了
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)


# 处理水果的类,后面改成宠物连连看了
class Fruits:
    def __init__(self, start_x, start_y):
        # 保存游戏正真的左上角起始位置
        self.start_x, self.start_y = start_x, start_y
        # 存储数据为12行,16列
        self.row, self.col = 12, 16
        # 一个小图片区域的长宽
        self.fruit_x, self.fruit_y = 36, 32
        # 保存矩阵数据,预留为12行和16列
        self.data = [[0 for _ in range(self.col)] for _ in range(self.row)]

    def capture_game_data(self, threshold=0.99):
        # 将游戏界面截图一张,用于分析每个位置的水果
        capture_screen_win32('game', self.start_x, self.start_y, 608, 392)
        # 每个水果格子缓存图像数据的画布,每个格子长35,宽31,要存rgb值
        canvas = np.zeros((self.fruit_y, self.fruit_x, 3), dtype='uint8')
        # 读取游戏截图的彩色图数据
        game_rgb = cv2.imread('game.bmp')
        # 标记水果的编号
        flag_fruits = 1
        for f_y in range(self.row - 2):
            for f_x in range(self.col - 2):
                # 已经被标记的不用重复标记
                if self.data[f_y + 1][f_x + 1] == 0:
                    # 扣一个长方形,将原图数据缓存到canvas,要存rgb值
                    for c_y in range(self.fruit_y):
                        for c_x in range(self.fruit_x):
                            px, py = c_x + 44 * f_x, c_y + 40 * f_y
                            canvas[c_y, c_x, 0] = game_rgb[py, px, 0]
                            canvas[c_y, c_x, 1] = game_rgb[py, px, 1]
                            canvas[c_y, c_x, 2] = game_rgb[py, px, 2]
                    if np.mean(canvas) < 30.0:
                        continue  # 此处没有要消除的图片
                    # 匹配当前抠出的水果,通过z匹配得到该水果的所有位置
                    res = cv2.matchTemplate(game_rgb, canvas, cv2.TM_CCOEFF_NORMED)
                    loc = np.where(res >= threshold)  # 这个阈值很重要,要取合适的
                    for pt in zip(*loc[::-1]):
                        my, mx = (pt[1] + 10) // 40 + 1, (pt[0] + 10) // 44 + 1
                        if self.data[my][mx] == 0:
                            # 当前位置没有标记水果,则标记一下,加入判断避免重复标记
                            self.data[my][mx] = flag_fruits
                            # 下面的循环将剔除已经被标记的区域,之后的匹配更快
                            for c_y in range(self.fruit_y):
                                for c_x in range(self.fruit_x):
                                    px, py = pt[0] + c_x, pt[1] + c_y
                                    game_rgb[py, px, 0], game_rgb[py, px, 1], game_rgb[py, px, 2] = 0, 0, 0
                                    # cv2.putText(game_rgb, str(flag_fruits), (pt[0], pt[1] + 25), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
                    flag_fruits += 1  # 标记完一个水果就标记下一个
        print flag_fruits
        # cv2.imwrite('c3.png', game_rgb)

    def print_data(self):
        # 将数据存入文件,查看每个图片的编号和位置
        fw = open('data.txt', 'w')
        for mi in range(self.row):
            for mj in range(self.col):
                fw.write('%3i' % self.data[mi][mj])
            fw.write('\n')
        fw.close()

    def is_win(self):
        # 返回False表示已经胜利,返回True表示还要继续
        for win_y in range(self.row):
            for win_x in range(self.col):
                if self.data[win_y][win_x] > 0:
                    return True
        return False

    def click_fruits(self, click_y1, click_x1, click_y2, click_x2):
        # 根据编号位置,和图像起始位置,计算鼠标点击位置
        mouse_click(self.start_y + 40 * (click_y1 - 1) + 20, self.start_x + 44 * (click_x1 - 1) + 20)
        mouse_click(self.start_y + 40 * (click_y2 - 1) + 20, self.start_x + 44 * (click_x2 - 1) + 20)
        # 已经连线后,这两个点标记为空白,后面的计算才能正常
        self.data[click_y1][click_x1], self.data[click_y2][click_x2] = 0, 0

    def play_game(self):
        self.capture_game_data()
        self.print_data()
        # 这里分析数据来控制鼠标玩游戏
        while self.is_win():
            # 为每个编号的水果添加坐标数组
            num_list = {}
            # 遍历整个矩阵,找出那种需要拐弯相连的水果
            for play_y in range(1, self.row - 1):
                for play_x in range(1, self.col - 1):
                    tmp_num = self.data[play_y][play_x]
                    if tmp_num > 0:  # 当前位置有编号才行
                        if tmp_num in num_list:  # 判断字典key存在则追加,不存在则创建
                            num_list[tmp_num].append([play_y, play_x])
                        else:
                            num_list[tmp_num] = [[play_y, play_x]]
            # 遍历相同水果不同位置,看看能不能连起来
            for values in num_list.values():
                # 得到每种编号水果位置的个数
                val_len = len(values)
                for val_i in range(val_len):
                    y1, x1 = values[val_i][0], values[val_i][1]
                    # 确保该点没有被消除,因为运算过程中可能消掉
                    if self.data[y1][x1] > 0:
                        for val_j in range(val_i + 1, val_len):
                            y2, x2 = values[val_j][0], values[val_j][1]
                            # 确保该点没有被消除,因为运算过程中可能消掉
                            if self.data[y2][x2] > 0:
                                if self.connect_pos(y1, x1, y2, x2):
                                    break  # 两点被消,则直接进入下个循环
        print 'you are win...'

    def connect_pos(self, y1, x1, y2, x2):
        flag_ok = False
        if x1 == x2:  # |
            flag_ok = self.can_link_1(x1, min(y1, y2), max(y1, y2))
        elif y1 == y2:  # -
            flag_ok = self.can_link_2(y1, min(x1, x2), max(x1, x2))
        elif x1 > x2 and y1 > y2:  # \
            flag_ok = self.can_link_3(x2, y2, x1, y1)
        elif x1 < x2 and y1 < y2:  # \
            flag_ok = self.can_link_3(x1, y1, x2, y2)
        elif x1 > x2 and y1 < y2:  # /
            flag_ok = self.can_link_4(x2, y2, x1, y1)
        elif x1 < x2 and y1 > y2:  # /
            flag_ok = self.can_link_4(x1, y1, x2, y2)
        if flag_ok:  # 如果能连接则把2个点点开
            self.click_fruits(y1, x1, y2, x2)
        return flag_ok

    def can_link_1(self, x, y_min, y_max):  # |
        if self.can_link_y(x, y_min + 1, y_max):
            return True
        for tx in range(self.col):
            if tx == x:  # 跳过本列
                continue
            tx0, tx1 = tx, x
            if tx > x:
                tx0, tx1 = x + 1, tx
            flag_ok = 0
            if self.can_link_y(tx, y_min, y_max + 1):
                flag_ok += 1
            if self.can_link_x(y_min, tx0, tx1):
                flag_ok += 1
            if self.can_link_x(y_max, tx0, tx1):
                flag_ok += 1
            if flag_ok == 3:
                return True
        return False

    def can_link_2(self, y, x_min, x_max):  # -
        if self.can_link_x(y, x_min + 1, x_max):
            return True
        for ty in range(self.row):
            if ty == y:  # 跳过本列
                continue
            ty0, ty1 = ty, y
            if ty > y:
                ty0, ty1 = y + 1, ty
            flag_ok = 0
            if self.can_link_x(ty, x_min, x_max + 1):
                flag_ok += 1
            if self.can_link_y(x_min, ty0, ty1):
                flag_ok += 1
            if self.can_link_y(x_max, ty0, ty1):
                flag_ok += 1
            if flag_ok == 3:
                return True
        return False

    def can_link_3(self, x_min, y_min, x_max, y_max):  # \
        for ty in range(self.row):  # 纵向
            if ty < y_min:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, ty, y_min):
                    flag_ok += 1
                if self.can_link_y(x_max, ty, y_max):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif ty == y_min:
                flag_ok = 0
                if self.can_link_x(ty, x_min + 1, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_max, ty, y_max):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            elif ty > y_min and ty < y_max:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, y_min + 1, ty + 1):
                    flag_ok += 1
                if self.can_link_y(x_max, ty, y_max):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif ty == y_max:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max):
                    flag_ok += 1
                if self.can_link_y(x_min, y_min + 1, ty + 1):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            else:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, y_min + 1, ty + 1):
                    flag_ok += 1
                if self.can_link_y(x_max, y_max + 1, ty + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
        for tx in range(self.col):  # 横向
            if tx < x_min:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, tx, x_min):
                    flag_ok += 1
                if self.can_link_x(y_max, tx, x_max):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif tx == x_min:
                flag_ok = 0
                if self.can_link_y(tx, y_min + 1, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, tx, x_max + 1):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            elif tx > x_min and tx < x_max:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, x_min + 1, tx + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, tx, x_max):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif tx == x_max:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max):
                    flag_ok += 1
                if self.can_link_x(y_min, x_min + 1, tx + 1):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            else:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, x_min + 1, tx + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, x_max + 1, tx + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True

    def can_link_4(self, x_min, y_max, x_max, y_min):  # /
        for ty in range(self.row):  # 纵向
            if ty < y_min:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, ty, y_max):
                    flag_ok += 1
                if self.can_link_y(x_max, ty, y_min):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif ty == y_min:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max):
                    flag_ok += 1
                if self.can_link_y(x_min, ty, y_max):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            elif ty > y_min and ty < y_max:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, ty, y_max):
                    flag_ok += 1
                if self.can_link_y(x_max, y_min + 1, ty + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif ty == y_max:
                flag_ok = 0
                if self.can_link_x(ty, x_min + 1, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_max, y_min + 1, ty + 1):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            else:
                flag_ok = 0
                if self.can_link_x(ty, x_min, x_max + 1):
                    flag_ok += 1
                if self.can_link_y(x_min, y_max + 1, ty + 1):
                    flag_ok += 1
                if self.can_link_y(x_max, y_min + 1, ty + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
        for tx in range(self.col):  # 横向
            if tx < x_min:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, tx, x_max):
                    flag_ok += 1
                if self.can_link_x(y_max, tx, x_min):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif tx == x_min:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max):
                    flag_ok += 1
                if self.can_link_x(y_min, tx, x_max):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            elif tx > x_min and tx < x_max:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, tx, x_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, x_min + 1, tx + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True
            elif tx == x_max:
                flag_ok = 0
                if self.can_link_y(tx, y_min + 1, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, x_min + 1, tx + 1):
                    flag_ok += 1
                if flag_ok == 2:
                    return True
            else:
                flag_ok = 0
                if self.can_link_y(tx, y_min, y_max + 1):
                    flag_ok += 1
                if self.can_link_x(y_min, x_max + 1, tx + 1):
                    flag_ok += 1
                if self.can_link_x(y_max, x_min + 1, tx + 1):
                    flag_ok += 1
                if flag_ok == 3:
                    return True

    def can_link_y(self, x, y_min, y_max):
        # 同x值,在y方向上检查连线,包括min,不包括max
        for ty in range(y_min, y_max):
            if self.data[ty][x] > 0:
                return False
        return True

    def can_link_x(self, y, x_min, x_max):
        # 同y值,在x方向上检查连线,包括min,不包括max
        for tx in range(x_min, x_max):
            if self.data[y][tx] > 0:
                return False
        return True


# 主程序
if __name__ == '__main__':
    # 截屏,默认为全屏
    capture_screen_win32('screen.bmp')
    # 读取屏幕的截图数据
    screen_gray = cv2.imread('screen.bmp', 0)
    # 读取一张图片,用于定位游戏位置
    fruits_pos = cv2.imread('pet.png', 0)
    res_pic = cv2.matchTemplate(screen_gray, fruits_pos, cv2.TM_CCOEFF_NORMED)
    _, pic_val, _, pic_loc = cv2.minMaxLoc(res_pic)
    if pic_val < 0.95:
        print 'Please open the game...'
    else:
        # 创建一个对象,传入当前游戏的位置,需要抓点
        fruits = Fruits(pic_loc[0] - 204, pic_loc[1] + 52)
        # 开始玩游戏
        fruits.play_game()
